/**
 * 这个是基础的mixin 文件，里面有一些函数，用于处理统一格式的配置数据，
 * 使用的前提是 配置文件的格式得严格匹配
 * 需要时可参考 src\components\uniaxial-line\config\config.js  这个配置
 * 有特殊情况就覆写方法
 *
 * 更新：xtag 相关方法放入chart-base-xTag
 */
import STATIC_DATA from '@/common/staticData';
import advancedLinkageMixin from '@/components/common/advanced-linkage/advanced-linkage-mixin.js';
import autoRefreshMixin from '@/components/common/auto-refresh/auto-refresh-mixin.js';
import echartActionMixin from '@/components/common/echart/mixins/echart-action-mixin.js';
import multiParamsMixin from '@/components/multi-params/multi-params-mixin.js';
import winContainerMixin from '@/components/win-container/win-container-mixin.js';
import EventBus from '@/event-bus/EventBus.js';
import asTooltip from '@/mixin/asTooltip.js';
import baseFunctions from '@/mixin/base-functions.js';
import chartBaseFabric from '@/mixin/chart-base-fabric.js';
import chartBaseXMiner from '@/mixin/chart-base-xMiner.js';
import chartBaseXTag from '@/mixin/chart-base-xTag.js';
import chartBaseDac from '@/mixin/chart-base-dac.js';
import urlParamsMixin from '@/mixin/url-params/url-params-mixin.js';
import lowCodeMixin from '@/components/common/low-code/low-code-mixin.js';
import storage from '@/utils/ado-self/ado-storage';
import configMerge from '@/utils/config-merge.js';
import { EMIT_PARAMS_OBJ } from '@/utils/constant';
import { watch } from 'vue';
import { getLowCodeExpVal } from '@/components/common/low-code/low-code-util.js';
import {
  clone,
  convertFormData,
  debounce,
  getPageId,
  getVmPageStatus,
  isObject,
  isStaticData,
  isUndefinedNull,
  mergeObj,
  numFormat,
  objHas,
  permil,
  refactorApis,
  transformStaticDataByConfig,
  transformStaticDataByProperty,
  visualAction,
  camelCase,
  getCssStyle,
  isObjEqual,
} from '@/utils/util';
import { getWinRates } from '@/utils/util-common.js';
import { orderBy as _orderBy, difference as _difference } from 'lodash';
import { v4 as uuid4 } from 'uuid';
import { mapMutations, mapState } from 'vuex';
import { ABNORMAL_TYPE } from '@/utils/constant.js';

const xMiner = 'xMiner';
const xTag = 'xTag';
const xMinerPublicDatabase = 'xMinerPublicDatabase';
const xMinerRedisRealTime = 'xMinerRedisRealTime';

export default {
  emits: ['mounted'],
  props: {
    isDesignView: Boolean, // isDesignView(是否在编排面)
    isChildComponent: { // 是否子组件
      type: Boolean,
      default: false,
    },
    childIndex: { // 子组件索引
      type: Number,
      default: 0,
    },
    resData: { // 子组件使用父组件数据（卡片组件）时，父组件传数据给子组件
      type: Array,
      default: () => null,
    },
  },
  mixins: [
    baseFunctions,
    chartBaseXMiner,
    chartBaseXTag,
    chartBaseFabric,
    chartBaseDac,
    echartActionMixin,
    asTooltip,
    advancedLinkageMixin,
    winContainerMixin,
    multiParamsMixin,
    autoRefreshMixin,
    urlParamsMixin,
    lowCodeMixin,
  ],
  data() {
    const { $isVisualVappShowLoadingProgress } = window._adoPremise;
    return {
      config: null, // 以前一直不显式声明这个变量，总觉得有问题
      isVisualVappShowLoadingProgress: $isVisualVappShowLoadingProgress === false ? false : true, // xvue控制loading总开关
      fontStyle: {
        noData: {},
      },
      componentStyleClass: window._adoPremise.uc.theme || 'light',
      componentStyle: window._adoPremise.uc.theme || 'light',
      params: {},
      lastConfigLinkage: [],
      isFirstChangeWidget: true,
      pageStatus: null,
      hasWatchEmitComponentStatusTrue: false, // 是否已经watch过 emitComponentStatus 为true
      isFirstSend: true, // 是否第一次发出事件(只对发出组件有意义)
      historyTimer: null,
      timers: {}, // 组件中的计时器，需要在beforeDestory中清除掉，防止切换页面或组件被销毁后还继续执行
      convertedData: null, // 图表组件转换后的数据
      chartIsCreated: false, // echart实例化完成标志
      apiResult: [],
      xvueMutationObserver: null,
      receivedEvents: [], // 接收到的事件，用于高级事件联动设置url传参
      curComp: null,
      curPageId: null,
      extStylesTimer: null,
      clickLock: false, // 点击锁定，子节点点击后，如果父亲节点也有事件绑定，会同时执行两个事件，在子节点中加锁，在父节点中判断锁状态再执行，防止事件冲突
      isDataAbnormal: false, // 判断查询数据时接口是否异常
      abnormalType: '', // 数据异常的类型
    };
  },
  provide() {
    const self = this;
    return {

      // 在子孙组件中获取当前组件配置
      getCompConfig() {
        return self.config;
      },
    };
  },

  computed: {
    ...mapState({
      commonParams: state => state.VisualComponentsModule.commonParams,
      visualParamsOutObj: state => state.VisualComponentsModule.visualParamsOutObj,
      componentsHistory: state => state.VisualComponentsModule.componentsHistory,
      echartActions: state => state.VisualComponentsModule.echartActions,
      resizeCount: state => state.VisualComponentsModule.resizeCount,
      needClearOutEvent: state => state.VisualComponentsModule.needClearOutEvent,
      allComponentsReady: state => state.VisualComponentsModule.allComponentsReady, // 所有组件第一次准备就绪
      curEvtEmitObj: state => state.VisualComponentsModule.curEvtEmitObj,
    }),
    emitComponentStatus() {
      const state = this.$store.state.VisualComponentsModule;

      if (this.isChildComponent) {
        return true;
      }

      // 原写得state.pageId有问题  初始取的是上一个页面的id
      if (!this.pageId) {
        return false;
      }
      let canEmit = true; // 组件能否发出事件
      const { pageId } = this; // 页面的id：state.pageId

      const pageComponent = state.emitComponentStatusObj[pageId];
      if (!pageComponent) {
        return canEmit;
      }
      for (const key in pageComponent) {
        // 存在有异步没加载完
        if (pageComponent[key]) {
          canEmit = false;
        }
      }
      return canEmit;
    },
    adjustClass() {
      const adaptSetting = this.getAdaptSetting();
      let str = '';
      if (adaptSetting) {
        if (adaptSetting.type === '0') {
          str = 'totalAdjust';
        } else if (adaptSetting.type === '1') {
          str = 'columnAdjust';
        } else if (adaptSetting.type === '2') {
          str = 'rowAdjust';
        } else { }
      }
      return str;
    },

    // 背景色透明度
    boxBgColor() {
      const { echart, backgroundColor } = this.config.config.property;
      if (echart && echart.backgroundColor) {
        return { backgroundColor: `${echart.backgroundColor} !important` };
      }
      if (backgroundColor) {
        return { backgroundColor: `${backgroundColor} !important` };
      }
      return { backgroundColor: `${this.getValByCompStl('#fff', '#0d263a', '#232323')} !important` };
    },
    paramsObj() {
      const params = {};
      (this.params?.params || []).forEach(item=>{
        params[item.code] = item.value;
      });
      return params;
    },
  },
  watch: {

    // 发出组件状态，是否已经全部就绪 //只会在初始化发出
    emitComponentStatus(newVal, oldVal) {
      if (this.ignoreBaseEmitComponentStatus) {
        return;
      }
      if (this.hasWatchEmitComponentStatusTrue) {
        return;
      }
      if (newVal && newVal !== oldVal) {
        // 记录所有组件第一次准备就绪标记
        if (newVal) {
          clearTimeout(this.timers.setCompReady);
          this.timers.setCompReady = setTimeout(() => {
            this.setState({ allComponentsReady: true });
          }, 200);
        }

        this.hasWatchEmitComponentStatusTrue = true;
        const pageStatus = getVmPageStatus(this);
        this.updateParams(pageStatus);
        if (!this.isRealTime) {
          this.debounceGetData();
        }

        // 属性面板设置了实时数据后
        if (this.isPropertyRealTime === false) {
          this.debounceGetPropertyData();
        }
      }
    },

    // 本地联动监控
    visualParamsOutObj(newVal) {
      if (window.isLocalEnv) {
        if (newVal.sentByWid === this.widgetInstanceId) {
          return;
        }
        this.acceptEvtMsg({}, '', newVal);
      }
    },

    chartIsCreated(isCreated) {
      if (isCreated) {
        this.mouseoverInit();
        this.onEchartActions();
      }
    },

    // 组件销毁时，将组件的发出事件清除，其他组件去除相应事件重新加载
    needClearOutEvent(evItem) {
      if (!evItem || !evItem.key) {
        return;
      }
      const inEvents = this.config.events.filter(n => n.type === 'in' && n.key);
      if (inEvents.find(n => n.key === evItem.key)) {
        const pageId = getPageId(this);
        const { tenantId } = window._adoPremise;
        const sKey = `${pageId}_${tenantId}`;
        const pageStatus = storage.session.get(sKey) || {};
        this.loadWidget(pageStatus);
      }
    },

    // 自动清除点击锁
    clickLock(isLocked) {
      if (isLocked) {
        this.timers.clickLock = setTimeout(() => {
          this.clickLock = false;
        }, 50);
      }
    },

    pageId(newId, oldId) {
      this.clearImgBlogCache();
    },

  },

  created() {
    this.initBodyClass();
    this.bindPageOpen();
    this.bindPageChange();
    this.bindPageResize();
    this.initEvtWatch();
    this.watchXvueContainerExpend();
    this.initPreviewVm();
    if (window.isLocalEnv) {
      const pageId = getPageId();
      this.setPageId(pageId);
      if (!this.pageId) {
        this.pageId = pageId;
      }
    }

    // 防抖函数写在created() 中，防止多个相同的组件使用同一个方法（ methods 中定义的方法是共享的）
    this.debounceGetData = debounce(() => {
      this.getChartData && this.getChartData();

      // 表格相关配置拿到数据后 需要再次执行更新配置的方法才能更新
      this.updateConfigDataValue && this.updateConfigDataValue();
    }, 200);

    if (this.widgetInstanceId) {
      EventBus.$on(`vapp_changeWidget_${this.widgetInstanceId}`, obj => {
        this.changeWigetXYWH &&
          this.changeWigetXYWH({
            x: obj.x,
            y: obj.y,
            width: obj.w,
            height: obj.h,
            widgets: this.layouts,
          });
      });

      // 接收xvue发出的事件（在缩放分辨率后会发出）
      EventBus.$on(`resize-layout-widget-${this.widgetInstanceId}`, () => {
        this.onComponentResize(150);
        setTimeout(() => {
          this._updateComponentByResolution();
        }, 500);
      });
    }

    // 实时通讯组件与xvue绑定事件，接收xvue传来的数据变更视图
    EventBus.$on(`${this.widgetInstanceId}-accept-realtime-data`, data => {
      if (this.config.config.data.dataSourceType !== 'kafka' && document.hidden) {
        return;
      }

      // 统一处理 websocket 类数据异常逻辑
      const { datalist, index } = data;
      this.resetAbnormalType();

      // 通过判断index只针对数据面板判断字段缺失，有特殊情况组件内单独处理
      !index && this.reportIfFieldsError(convertFormData(this.config.config.data.tableData), datalist[0]?.data);
      this.handleRealTimeCommunication && this.handleRealTimeCommunication(data);
    });

    // 本地模拟xvue的emit事件，用于原理
    if (window.isLocalEnv) {
      this.emit = this._xVueEmit;
      this.hasPageWidgetsMounted = true; // 本地都是true
    }
  },
  mounted() {
    this.curPageId = this.pageId; // xvue给组件的属性// 切换页面后 beforeDestroy() 的this.pageId 不是当前的pageId，而是下一个选中的页面的pageId

    // 子组件加载完毕，父组件需要后续操作一些逻辑
    if (this.isChildComponent) {
      this.$emit('mounted', this);
    }
    // 代替vapp-helper执行组件自身的初始化
    if (window.isLocalEnv) {
      this.loadWidget();
    }
  },
  beforeUnmount() {
    this.deletePageStatus();
    this.clearTimers();
    this.clearChart();
    this.clearEvents(this.config.events);
    if (this.widgetInstanceId) {
      EventBus.$off(`vapp_changeWidget_${this.widgetInstanceId}`);
      EventBus.$off(`vis_getComponentData_${this.widgetInstanceId}`);
      EventBus.$off(`curPreviewVm_${this.widgetInstanceId}`);
    }
    EventBus.$off(`resize-layout-widget-${this.widgetInstanceId}`);

    EventBus.$off(`${this.widgetInstanceId}-accept-realtime-data`);

    this.deleteComponentStatus &&
      this.deleteComponentStatus({
        pageId: this.curPageId,
        widgetInstanceId: this.widgetInstanceId,
      });
    this.deleteComponentsData(this.widgetInstanceId);

    window.removeEventListener('resize', this.onComponentResize);

    // 移除当前tooltip鼠标经过绑定事件
    this.$el.removeEventListener('mouseenter', this.widgetOnMouseEnter);

    // 移除当前组件点击事件绑定
    this.$el.removeEventListener('click', this._vmClick);

    // 解绑mutationObserver监听
    this.xvueMutationObserver && this.xvueMutationObserver.disconnect && this.xvueMutationObserver.disconnect();

    // 移除当前组件点击事件绑定
    this.$el.removeEventListener('click', this._vmClick);
  },

  methods: {
    ...mapMutations([
      'setPageId',
      'deleteComponentStatus',
      'setState',
      'setComponentsData',
      'deleteComponentsData',
      'setComponentStatus',
      'setRealTimeApiData',
    ]),

    // 扩展样式设置
    setExtStyles() {
      const { extStyles } = this.config.config.property;
      if (!extStyles) {
        return;
      }

      clearTimeout(this.extStylesTimer);
      this.extStylesTimer = setTimeout(() => {
        const oldStyles = [...this.$el.childNodes].filter(n => n.nodeType === Node.ELEMENT_NODE && n.classList.contains('extStyles'));
        oldStyles.forEach(oldStyle => {
          this.$el.removeChild(oldStyle);
        });
        const clsName = `extStyle_${this.widgetInstanceId}`;
        this.$el.classList.add(`extStyle_${this.widgetInstanceId}`);
        extStyles.forEach(extStyle => {
          if (!extStyle.switch) {
            return;
          }
          if (extStyle.condition === false) {
            return;
          }

          const style = document.createElement('style');
          const attr = document.createAttribute('class');
          attr.value = 'extStyles';
          style.setAttributeNode(attr);

          // 自适应
          let cssStyle = {};
          extStyle.styles.forEach(n => {
            cssStyle[camelCase(n.type)] = n.value;
          });
          cssStyle = getCssStyle(cssStyle);

          // 对象转字符串
          const arr = Object.entries(cssStyle).map(([key, val]) => {
            const cssKey = key.replace(/([A-Z])/g, '-$1').toLowerCase(); // 驼峰转小写 fontSize => font-size
            let cssVal = String(val);
            if (!cssVal.includes('!important')) {
              cssVal = `${cssVal} !important`;
            }
            return `${cssKey}: ${cssVal}`;
          });
          const str = arr.join(';');

          // 选择器也就是extStyle.extStyleOption的值，格式统一用', '隔开
          let selector = extStyle.extStyleOption;
          if (selector === 'diy') {
            selector = extStyle.elSelector || '';
          }
          const qsArr = selector.split(', ');
          qsArr.forEach( qs => {
            if (qs.includes('[root]')) {
              const _qs = qs.replace('[root]', `.${clsName}`);
              style.textContent += `${_qs} { ${str} }`;
            } else {
              style.textContent += `.${clsName} ${qs} { ${str} }`;
            }
          });
          this.$el.appendChild(style);
        });
      }, 1);
    },

    // 获取配置页面的参数  事件接收的参数不变
    _getCompConfig() {
      return this.config;
    },
    getParams(_data) {
      let { data } = this.config.config;
      if (_data) {
        data = _data;
      }

      // 适配左xx右xx组件data格式
      if (this.curComp) {
        data = this.config.config.data[this.curComp];
      }

      const { dataSourceType } = data;
      let params = null;
      if (!dataSourceType) {
        return null;
      }
      const arr = ['xMiner', 'xMinerPublicDatabase', 'fabric', 'xVueApi', 'Dac'];
      if (arr.includes(dataSourceType)) {
        params = {
          ...this.params,
          apiCode: data.apiCode, // 配置页面中选择的api编码
          propertyMapping: convertFormData(data.tableData), // 配置页面中 选择的呈现字段
        };
      } else if (dataSourceType === xTag) {
        params = this.getParamsXTag(data);
      } else {
        // 留空
      }
      return params;
    },

    /*
     *多值组件阈值样式处理
     *阈值弹窗与配置组件的颜色兼容
     */
    multiValStyle(item, key = 'value') {
      const { thresholdDataType } = this.config.config.property;
      let obj = {
        ...this.getCssStyle('value'),
      };
      if (key === 'unit') {
        obj = {
          ...this.getCssStyle('unit'),
        };
      }

      if (this.isThreshold) {
        if (thresholdDataType === 'number') {
          this.threshold.map(child => {
            item.value !== null &&
              this.getValueNum(item.value) >= Number(child.min) &&
              this.getValueNum(item.value) < Number(child.max) ?
              (obj.color = child.color) :
              null;
          });
        } else {
          this.threshold.map(child => {
            item.value !== null && child.text === item.value ? (obj.color = child.color) : null;
          });
        }
      }
      return obj;
    },
    getValueNum(str) {
      const ret = `${str}`;
      if (ret.indexOf(',') > -1) {
        return Number(ret.replace(/,/gm, ''));
      }
      if (!isNaN(Number(str))) {
        if (str === null) {
          return null;
        } else if (str === '') {
          return NaN;
        } else {
          return Number(str);
        }
      }
      return str;
    },

    /*
     * 将参数保存到xvue接口 Vue.prototype.$xvuePageComponentApiMap = [{instanceId: "", apis : [{apicode: "", param : ""}]}]
     */
    addReqParamsToXvue(params = {}) {
      const _params = JSON.parse(JSON.stringify(params));

      let { $xvuePageComponentApiMap: xvueApiMap } = window.xVueWeb.app.config.globalProperties || {};
      if (!xvueApiMap || !Array.isArray(xvueApiMap)) {
        xvueApiMap = [];
      }
      const instanceId = this.widgetInstanceId;
      const { curComp } = this;
      const apis = curComp ? { ..._params, curComp } : _params;
      delete apis.propertyMapping;
      const targetInstanceIndex = xvueApiMap.findIndex(item => item.instanceId === instanceId);
      const _apisTooltip = this.getTooltipApis() || []; // 获取tooltip的请求参数
      const refactorApi = refactorApis(apis);
      if (targetInstanceIndex === -1) {
        xvueApiMap.push({ instanceId, apis: [refactorApi, ..._apisTooltip] });
      } else {
        let curApis = xvueApiMap[targetInstanceIndex].apis || [];
        curApis = curApis.map(ele => {
          const res = refactorApis(ele);
          return res;
        });

        // 左表右xx
        if (curComp) {
          for (let i = 0; i < curApis.length; i++) {
            if (curApis[i].curComp === curComp) {
              curApis.splice(i, 1);
              i--;
            }
          }
          curApis = [...curApis, refactorApi, ..._apisTooltip];
        } else {
          // 非左表右xx
          curApis = [refactorApi, ..._apisTooltip];
        }
        xvueApiMap[targetInstanceIndex].apis = curApis;
      }

      window.xVueWeb.app.config.globalProperties.$xvuePageComponentApiMap = [...xvueApiMap];
    },

    // 获取正确的参数-请求接口前处理下参数
    getCorrectParams(params, dataSourceType, data) {
      // 数据源为xVueApi, 返回监控函数供源数据改变时做数据同步
      if (dataSourceType === 'xVueApi') {
        return {
          ...params,
          vm: this,
          watchFn: () => {
            if (this.getChartData) {
              this.getChartData();
            }
          },
        };
      }

      let resParams = !dataSourceType ? params : this[`reviseParams${dataSourceType}`](params);
      if (this[`addCommonSourceParams${dataSourceType}`]) {
        resParams = this[`addCommonSourceParams${dataSourceType}`](resParams, data);
      }
      if (dataSourceType === 'xMiner') {
        this.addReqParamsToXvue(resParams);
      } else if (dataSourceType === 'xMinerPublicDatabase' || dataSourceType === 'xTag') {
        this.addReqParamsToXvue({});
      } else { }

      // 获取组件信息，给后续的checkApiResTotal函数使用
      if (this.layouts && this.isDesignView) {
        const compInfo = this.layouts.find(n => n.widgetInstanceId === this.widgetInstanceId);
        let { instanceName, displayName } = compInfo || {};
        instanceName = instanceName || '';
        displayName = displayName || '';
        let compName = instanceName;
        if (!instanceName.includes(displayName)) {
          compName = `${instanceName} (${displayName})`;
        }
        resParams.compInfo = { wid: this.widgetInstanceId, componentName: compName };
      }
      if (process.env.NODE_ENV === 'development') {
        resParams.compInfo = { wid: this.widgetInstanceId, componentName: this.widgetInstanceId };
      }

      return resParams;
    },

    /**
     * xMiner公共数据库需要加上datasourceCode和isCommonDatasource参数
     * @param {*} params
     * @param {*} data 新增data参数以适配api数据不存在this.config.config.data的组件如hover-text
     */
    addCommonSourceParamsxMinerPublicDatabase(params, data) {
      const { curComp } = this;
      let { dataSource } = data || this.config.config.data;
      if (curComp) {
        dataSource =
          (data && data[this.curComp] && data[this.curComp].dataSource) ||
          this.config.config.data[this.curComp].dataSource;
      }
      params.isCommonDatasource = true;
      params.datasourceCode = dataSource;
      return params;
    },

    // 清除多项数据旧数据
    clearMultiValueResultWithIcon() {
      this.showData.forEach(item => {
        item.value = '';
        item.style = '';
        item.threImgUrl = '';
      });
    },

    // 清除多项数据旧数据
    clearMultiValueResultWithoutIcon() {
      this.showData.forEach(item => {
        item.name = '';
        item.value = '';
        item.unit = '';
        item.style = '';
      });
    },

    _getShowData(isIcon, result) {
      let showData;
      if (this.isDynamicAdapt) {
        if (isIcon) {
          showData = this.configData.filter(item => result.map(data => String(data.s)).includes(item.name));
        } else {
          showData = Array.from(this.configData.slice(0, Math.min(this.count, result.length)));
        }
      } else {
        showData = Array.from(this.configData);
      }
      return clone(showData);
    },

    _getDataUnit(resItem) {
      let unit;
      if (resItem) {
        if (!isUndefinedNull(resItem.unit)) {
          unit = numFormat(resItem.unit, true);
        } else {
          unit = numFormat(this.config.config.property.unit, true);
        }
      } else {
        unit = numFormat(this.config.config.property.unit, true);
      }
      return unit;
    },

    // 处理多项数据返回结果公共方法
    handleMultiValueResult(result, isIcon) {
      if (isIcon) {
        this.clearMultiValueResultWithIcon();
      } else {
        this.clearMultiValueResultWithoutIcon();
      }

      // 泰国需求动态适配 打开则开启动态适配，如果传值数量小于设置数量，则以传值为准显示；关闭则依据设置的参数为准
      this.showData = this._getShowData(isIcon, result);
      this.showData.forEach((data, i) => {
        // 判断是否带图标 => 名称和数值来源
        if (isIcon) {
          this._handleIsIconData(data, result);
        } else {
          data.name = result[i] ? result[i].s : '';
          data.value = result[i] ? result[i].y : '';
          data.extra = result[i] ? result[i].y1 : '';
          data.unit = this._getDataUnit(result[i]);
        }
        data.value = data.value === null ? this.config.config.property.nullSet : data.value;
        data.orgValue = data.value; // 记录原值
        // 将结果变为千分位值
        data.value = numFormat(data.value, true);
        data.orgExtra = data.extra; // 记录原附加值
        // 附加值呈现绝对值
        data.extra = this._getDataExtra(data);
      });

      // 多值带图标排序
      if (isIcon && this.multiSortBy !== 'name') {
        this._sortMultiData(result);
      }

      // 根据阈值更新数据样式和图标
      if (this.isThreshold) {
        this.showData.forEach(n => {
          this._updateDataByThreshold(n, isIcon);
        });
      }
    },

    _handleIsIconData(data, result) {
      const element = result.find(item => String(item.s) === data.name);
      data.value = element ? element.y : '';
      data.extra = element ? element.y1 : '';
      const hasDbUnit = element && !isUndefinedNull(element.unit);
      data.unit = hasDbUnit ? numFormat(element.unit, true) : numFormat(data.unit, true);

      // 新增的附加值，填入data
      for (const key in element) {
        if (Object.hasOwnProperty.call(element, key)) {
          const value = element[key];
          if (/^y\d+$/.test(key) && key !== 'y1') {
            data[key] = value;
          }
        }
      }
    },

    _getDataExtra(dataItem) {
      let extra;
      if (dataItem.isAbsoluteValue && dataItem.extra) {
        extra = numFormat(Math.abs(dataItem.extra), true);
      } else {
        extra = numFormat(dataItem.extra, true);
      }
      return extra;
    },

    _sortMultiData(result) {
      let _result = result;
      switch (this.multiSortBy) {
        case 'y_desc':
          _result = _orderBy(result, ['y'], ['desc']);
          break;
        case 'y_asc':
          _result = _orderBy(result, ['y'], ['asc']);
          break;
        case 'api':
          break;
        default:
          break;
      }
      const newShowData = [];
      for (let i = 0; i < _result.length; i++) {
        const n = _result[i];
        n.y = n.y === null ? this.config.config.property.nullSet : n.y;
        const item = this.showData.find(n1 => String(n.s) === n1.name && !newShowData.some(n2 => n2.name === n1.name));
        if (item) {
          item.value = numFormat(n.y, true);
          newShowData.push(item);
        }
        if (newShowData.length >= this.showData.length) {
          break;
        }
      }
      this.showData = newShowData;
    },

    // 根据阈值更新数据样式和图标
    _updateDataByThreshold(data, isIcon) {
      if (this.isThreshold) {
        const { thresholdDataType } = this.config.config.property;
        this.threshold.forEach(item => {
          if (
            data.value !== null &&
            ((thresholdDataType === 'number' &&
              this.getValueNum(data.value) >= Number(item.min) &&
              this.getValueNum(data.value) < Number(item.max)) ||
              (thresholdDataType === 'string' && data.value !== '' && data.value === item.text))
          ) {
            // 改变颜色
            data.style = {
              value: {
                ...this.style[this.componentStyle].value,
                color: item.color,
              },
              unit: {
                ...this.style[this.componentStyle].unit,
                color: item.color,
              },
            };

            // 改变图标
            if (isIcon) {
              // 该方法在icon-chart-base.js里
              this.updateImgUrlThreshold(data, item);
            }
          }
        });
      }
    },

    // 更新部分配置的属性
    updateConfigProperty() {
      const { property } = this.config.config;
      const excludes = ['pageId', 'layout'];
      Object.entries(property).map(([i]) => {
        if (!excludes.includes(i)) {
          this[i] = property[i];
        }
      });
      this.componentStyleClass = this.componentStyle || 'light';

      // 组件的标题配置支持国际化
      this.title = this.$demoi18n(this.title);
      property.title = this.$demoi18n(property.title);
    },

    // xvue 提供的方法，用于处理配置中的事件的监听
    acceptEvtMsg(paramters, key, obj) {
      this.checkUpdateParams(obj);
    },

    // 更新参数前检查下是否和本组件有关
    checkUpdateParams(obj) {
      const _obj = obj || {};
      let flag = false;
      for (const key in _obj) {
        if (key.indexOf(EMIT_PARAMS_OBJ) > -1) {
          const o = _obj[key];
          if (o.eventKey) {
            const event = this.config.events.find(e => e.type === 'in' && e.key === o.eventKey);
            if (event) {
              flag = true;
            }
            break;
          }
        }
      }
      if (flag) {
        this.updateRequestBody();
        this.updateParams(_obj);

        // 实时通讯组件接收到参数后不能主动去获取数据
        if (!this.isRealTime) {
          this.debounceGetData();
        }
      }
    },

    // 更新配置的数据 获取配置页面的参数
    updateConfigData() {
      this.params = this.getParams();
      this.addCommonParams();
      this.addAdvancedLinkageParams();
    },

    // 更新 请求body体内的其他参数
    updateRequestBody() {
      if (this.params?.pageIndex) {
        this.params.pageIndex = 1;

        // vxe-table页码兼容
        if (this.tablePage && this.tablePage.currentPage) {
          this.tablePage.currentPage = 1;
        }

        // element-ui表格页码兼容
        if (this.page && this.page.pageIndex) {
          this.page.pageIndex = 1;
        }
      }
    },

    /*
     *更新 api 请求参数（事件的key 以及value） 目前只适配xMiner的入参
     * updateParamsxMiner updateParamsxTag 相关方法移入对应base文件
     */
    updateParams(_obj) {
      let { dataSourceType } = this.config.config.data || {}; // xMiner/xTag
      if (dataSourceType === 'xVueApi') {
        return {};
      }

      let obj = _obj;
      if (!obj || Object.keys(obj).length === 0) {
        obj = getVmPageStatus(this);
      }

      // 无数据面板的组件，需要添加到comps数组，触发高级tooltip联动
      const comps = [
        'VisualComponentsHoverText',
        'JumpComponent',
        'SimpleTabContainer',
        'FlowTabContainerDiy',
        'ElasticContainer',
      ];
      if (comps.includes(this.$options.name)) {
        this.addUrlParams();
        this.updateParamsxMiner(obj);
        this.updateParamsDac(obj);
        this.updateParamsKey(obj);
        this.updatePageParams();
        this.filterParams();
      }
      if (!this.config.config.data) {
        return {};
      }

      if (dataSourceType === xMinerRedisRealTime) {
        return {};
      }

      // 适配左xx右xx组件data格式
      if (this.curComp) {
        dataSourceType = this.config.config.data[this.curComp].dataSourceType;
      }

      // 修正传参
      if (this.params && dataSourceType && dataSourceType !== 'xTag') {
        delete this.params.agg;
        delete this.params.conditions;
        delete this.params.sqlParams;
        delete this.params.customFields;
      }

      this.receivedEvents = [];
      if (!dataSourceType || isStaticData(dataSourceType)) {
        this.addNormalEvents(obj);
        this.addUrlParams();
        this.addCommonParams();
        this.addAdvancedLinkageParams();
        this.updateParamsKey(obj);
      } else {
        this.addUrlParams();
        if (dataSourceType === xMinerPublicDatabase) {
          this[`updateParams${xMiner}`](obj);
        } else {
          this[`updateParams${dataSourceType}`] && this[`updateParams${dataSourceType}`](obj);
        }
        this.addCommonParams();
        this.addAdvancedLinkageParams();
        this.updateParamsKey(obj);
      }
      this.updatePageParams();
      this.filterParams();

      return this.params; // 返回值，给配置面板用
    },

    // 参数过滤
    filterParams() {
      let params = this?.params?.params;
      if (params) {
        // this.params.params参数去重
        params = params.filter((item, index, arr) => arr.findIndex(obj => isObjEqual(obj, item)) === index);

        // 去除带低代码表达式的参数（变量不存在）
        const _params = [];
        params.forEach(n => {
          const value = getLowCodeExpVal({ expression: n.value });
          if (typeof value !== 'string' || !value.startsWith('{{')) {
            n.value = value;
            _params.push(n);
          }
        });

        this.params.params = _params;
      }
    },

    // 增加分页条件（由分页组件发出）
    updatePageParams() {
      if (!this.params) {
        return;
      }

      const obj = getVmPageStatus(this);
      const matchEvents = this.getMatchEvents(obj);

      matchEvents.forEach(n => {
        if (n.pagination) {
          this.params.pageIndex = n.pagination.pageIndex;
          this.params.pageSize = n.pagination.pageSize;
          this.params.total = true;
        }
      });
    },

    // 清除事件监听
    clearEvents(events) {
      if (events) {
        events.forEach(event => {
          EventBus.$off(event.eventName);
        });
      }
    },

    // 处理 数字 千分位加逗号 ,
    handleNumberThousandSeparator(val) {
      if (val) {
        const reg = /(?=(?!\b)(\d{3})+$)/g;
        let showNumber = `${val}`;
        if (showNumber.includes('.')) {
          const showNumberSplit = showNumber.split('.');
          showNumberSplit[0] = showNumberSplit[0].replace(reg, ',');
          showNumber = `${showNumberSplit[0]}.${showNumberSplit[1]}`;
        } else {
          showNumber = showNumber.replace(reg, ',');
        }
        return showNumber;
      }
      return '';
    },

    /*
     * 对字符串里面的数字转千分符，
     * 如： 'hello 10000.23 world 9999' => 'hello 10,000.23 world 9,999'
     */
    permil(str) {
      return permil(str);
    },

    // 鼠标经过事件绑定
    mouseoverInit() {
      clearTimeout(this.timers.mouseoverTimer);
      this.timers.mouseoverTimer = setTimeout(() => {
        const chart = this.$refs.dataChart && this.$refs.dataChart.chart;
        const tooltipVm = this.$refs.Tooltip;
        const { echart = {} } = this.config.config.property;
        if (chart && tooltipVm) {
          chart.on('mouseover', params => {
            if (!['series', 'markPoint'].includes(params.componentType)) {
              return;
            }
            const { _tranXYAxis } = echart;

            // 柱形图横向
            window.visualComp._tranXYAxis = _tranXYAxis;

            const _params = { ...params };
            if (this.getTooltipVars) {
              // 通过父组件原始数据变量
              this.getTooltipVars(_params);
            }
            if (_tranXYAxis) {
              const v0 = params.value[0];
              const v1 = params.value[1];
              _params.value = [...params.value];
              _params.value[0] = v1;
              _params.value[1] = v0;
            }
            tooltipVm.chartSerieItem = _params;
            window.visualComp.tooltipEvtType = 'mouseover';
          });
          chart.on('mouseout', params => {
            tooltipVm.chartSerieItem = params;
            window.visualComp.tooltipEvtType = 'mouseout';
          });
          chart.on('mousemove', params => {
            window.visualComp.tooltipEvtType = 'mouseover';
          });
        }
      }, 1000);

      this.$el.addEventListener('mouseenter', this.widgetOnMouseEnter);
    },

    widgetOnMouseEnter() {
      let { tooltip } = this.config.config.property;
      if (this.curComp) {
        tooltip = tooltip[this.curComp];
      }
      if (tooltip) {
        let { isVisiable, isOriginVisiable } = tooltip;

        if (typeof isVisiable === 'undefined') {
          isVisiable = false;
        }
        if (typeof isOriginVisiable === 'undefined') {
          isOriginVisiable = true;
        }

        window.visualComp.tooltipIsOriginVisiable = isOriginVisiable;
        window.visualComp.tooltipIsVisiable = isVisiable;
      }
    },

    // 本地模拟xvue的emit事件，用于本地联动测试
    _xVueEmit(evtKey, outObj) {
      // 抄自xvue的emit事件
      const orgEvt = this.orgConfig.events.find(evt => evt.key === evtKey);
      const evt = this.config.events.find(n => n.id === orgEvt.id);

      // 改变状态
      const _outObj = {
        sentByWid: this.widgetInstanceId,
      };
      if (evt && evt.parameters && evt.parameters.length) {
        for (let i = 0; i < evt.parameters.length; i++) {
          const paramId = evt.parameters[i].id;
          const paramKey = evt.parameters[i].key;
          const orgParamKey = orgEvt.parameters.find(p => p.id === paramId).key;
          _outObj[paramKey] = outObj[orgParamKey];
        }
      }

      // 发出消息
      this.setState({
        visualParamsOutObj: _outObj,
      });

      this.xVueEmit(evtKey, outObj);
    },

    // 合并基本配置和已保存配置，保证新增属性字段不缺失
    initConfig(dfConfig) {
      const _dfConfig = JSON.parse(JSON.stringify(dfConfig));
      const _savedConfig = JSON.parse(JSON.stringify(this.config || {}));
      const config = mergeObj(_dfConfig, _savedConfig);
      return config;
    },

    getTooltip() {
      if (!this.config) {
        return null;
      }
      let config = this.config.config.property.tooltip || {};
      if (this.curComp) {
        config = config[this.curComp] || {};
      }
      return {
        config,
        vars: {},
        params: this.params,
        parentComp: this,
      };
    },

    // 获取组件状态历史记录
    getCompHistory(index = false) {
      const histories = this.componentsHistory;
      const his = JSON.parse(JSON.stringify(histories));
      if (index === false) {
        return his;
      } else {
        // 待优化
        const wid = this.widgetInstanceId;
        if (!histories || !histories[wid]) {
          return null;
        }
        const { stores } = his[wid];
        return stores[stores.length - 1];
      }
    },

    // 保存组件状态历史记录
    saveCompHistory(keys = []) {
      clearTimeout(this.historyTimer);
      if (this.historyOpen) {
        const wid = this.widgetInstanceId;
        const histories = this.getCompHistory(false) || {};
        if (!histories[wid]) {
          histories[wid] = {
            index: 0, // 暂时没用
            stores: [],
          };
        }

        let _data = {};
        keys.forEach(key => {
          const hasKey = objHas(this._data, key);
          if (hasKey) {
            _data[key] = this._data[key];
          }
        });
        _data = JSON.parse(JSON.stringify(_data));

        histories[wid].stores.push(_data);
        histories[wid].stores = histories[wid].stores.slice(-1); // 最多只保留记录数
        this.setState({
          componentsHistory: histories,
        });
      }
    },

    // 加载组件历史状态记录
    loadCompHistory() {
      if (this.isDesignView) {
        return;
      }

      if (visualAction() === 'pageChange') {
        // 页面切换，清除历史记录
        this.clearCompHistory();
      } else if (this.historyOpen) {
        const history = this.getCompHistory(-1);
        if (history) {
          Object.entries(history).map(([i]) => {
            this[i] = history[i];
          });
        }
      } else { }
    },

    // 清空组件历史状态记录
    clearCompHistory() {
      const wid = this.widgetInstanceId;
      const histories = this.getCompHistory(false) || {};
      delete histories[wid];
      this.setState({
        componentsHistory: histories,
      });
    },

    // 等待准备状态完毕并发出请求，用于select等联动事件
    componentStatusReadyRun(fn, tryTimes = 30) {
      if (tryTimes <= 0) {
        return;
      }
      if (this.emitComponentStatus) {
        fn();
      } else {
        setTimeout(() => {
          this.componentStatusReadyRun(fn, tryTimes - 1);
        }, 100);
      }
    },

    // 获取当前组件的静态数据
    getStaticData() {
      const name = this.config.propertyPannelName;
      let data = STATIC_DATA[name];
      let tableData = [];

      // 适配左xx右xx组件data格式
      if (this.curComp) {
        const dataMap = {
          table: `config_${process.env.xvueNameSpace_}vxe_basic_page_table`,
          gantt: `config_${process.env.xvueNameSpace_}gantt_chart`,
          shape: `config_${process.env.xvueNameSpace_}shape_chart`,
        };

        data = STATIC_DATA[dataMap[this.curComp]];
        tableData = this.config.config.data[this.curComp].tableData;
        return transformStaticDataByConfig(data, tableData);
      }
      tableData = this.config.config.data.tableData;

      // 天气组件需要把
      if (this.config.propertyPannelName === `config_${process.env.xvueNameSpace_}weather`) {
        const propertyTableData = this.config.config.property.weatherIconMap;
        return transformStaticDataByProperty(data, tableData, propertyTableData);
      }
      return transformStaticDataByConfig(data, tableData);
    },

    // 获取当前组件属性上的静态数据
    getPropertyStaticData(tableData) {
      const data = STATIC_DATA[this.config.propertyPannelName];
      const propertyTableData = this.config.config.property.weatherIconMap;
      return transformStaticDataByProperty(data, tableData, propertyTableData);
    },

    // 在编排面时，把当前组件的数据放到Vuex，并发出事件
    sendDataToConfig(data) {
      if (this.isDesignView) {
        this.setComponentsData({
          widgetInstanceId: this.widgetInstanceId,
          componentData: data,
        });
        EventBus.$emit(`vis_getChartData${this.widgetInstanceId}`, {
          data,
        });
      }
    },

    // 当前组件属性面板realTimeApis请求的数据
    sendRealTimeApiDataToConfig(data) {
      if (this.isDesignView || process.env.NODE_ENV === 'development') {
        this.setRealTimeApiData({
          widgetInstanceId: this.widgetInstanceId,
          componentData: data,
        });
      }
    },

    // 将转换后的数据拿去渲染图表
    generateChart(convertedData) {
      this.convertedData = convertedData;
      if (convertedData) {
        this.isNoData = false;
      } else {
        this.isNoData = true;
      }

      // 拿着数据和配置画图去了
      let { data } = this.config.config;

      // 适配左xx右xx组件data格式
      if (this.curComp) {
        data = this.config.config.data[this.curComp];
      }
      this.$refs.dataChart && this.$refs.dataChart.generateChart(convertedData, this.config.config.property, data);
    },
    resizeChart() {
      let { data } = this.config.config;

      // 适配左xx右xx组件data格式
      if (this.curComp) {
        data = this.config.config.data[this.curComp];
      }
      if (this.convertedData) {
        this.$refs.dataChart.getQuotaChartWAndQuotaChartH();
        this.$refs.dataChart.generateChart(this.convertedData, this.config.config.property, data);
      }
    },

    _updateParamsByWidForXtag(obj, compParams) {
      const objKey = Object.keys(obj).find(n => n.includes(EMIT_PARAMS_OBJ));
      const { parameters, widgetInstanceId, type, keys } = obj[objKey];

      // --- 整理 conditions
      let conditions = compParams.conditions || [];
      conditions = conditions.filter(n => n.wid !== widgetInstanceId);
      const item = { wid: widgetInstanceId, [type]: [] };
      const arr = [];

      if (type === 'terms' || type === 'match') {
        Object.entries(parameters).map(([i]) => {
          // 用 [] 包装 数据
          let value = parameters[i];
          if (!Array.isArray(value)) {
            value = [value];
          }
          i && arr.push({ [i]: value });
        });
        item[type] = arr;
        conditions.push(item);
      }

      if (type === 'range') {
        if (keys[0]) {
          arr.push({
            [keys[0]]: {
              gte: parameters[keys[0]],
              lte: parameters[keys[1]],
            },
          });
        }
        item.range = arr;
        conditions.push(item);
      }
      compParams.conditions = conditions;

      // --- 整理 sqlParams
      let sqlParams = compParams.sqlParams || [];
      sqlParams = sqlParams.filter(n => n.wid !== widgetInstanceId);
      Object.entries(parameters).map(([i]) => {
        // 判断值是否为数组，不是则转为数组
        let value = parameters[i];
        if (!Array.isArray(value)) {
          value = [value];
        }
        const sqlItem = { wid: widgetInstanceId, key: i, value: value };
        sqlParams.push(sqlItem);
      });
      compParams.sqlParams = sqlParams;
    },

    /*
     * 根据请求对象中的组件id获取请求传参
     * 参数存放在内部，不放在外部，触发参考slide-fitler组件
     */
    updateParamsByWid(obj, dataSourceType, compParams) {
      const objKey = Object.keys(obj).find(n => n.includes(EMIT_PARAMS_OBJ));
      if (!objKey) {
        return false;
      }
      const { eventKey, parameters, widgetInstanceId, type, keys, sendTime = 0 } = obj[objKey];
      if (!widgetInstanceId || !parameters) {
        return false;
      }

      const hasEvent = this.config.events.find(n => n.type === 'in' && n.key === eventKey);
      if (hasEvent) {
        const arr = ['xMiner', 'xMinerPublicDatabase', 'fabric', 'Dac'];
        if (arr.includes(dataSourceType)) {
          let params = compParams.params || [];
          params = params.filter(n => n.wid !== widgetInstanceId);
          Object.entries(parameters).map(([i]) => {
            i && params.push({ code: i, value: parameters[i], wid: widgetInstanceId, sendTime });
          });
          compParams.params = params;
        }

        if (dataSourceType === xTag) {
          this._updateParamsByWidForXtag(obj, compParams);
        }
      }
      return true;
    },

    // 销毁组件中的setTimerout延时任务
    clearTimers() {
      Object.entries(this.timers).map(([i]) => {
        clearTimeout(this.timers[i]);
      });
    },

    // 注销组件前把echart实例销毁，节省内存
    clearChart() {
      let chart;
      if (this.$refs.dataChart && this.$refs.dataChart.chart) {
        chart = this.$refs.dataChart.chart;
      }
      if (chart) {
        if (chart.off) {
          chart.off('mouseover');
          chart.off('mouseout');
          chart.off('mousemove');
          chart.off('dataZoom');
          chart.off('legendselectchanged');
        }
        if (chart.dispose) {
          chart.dispose();
        }
      }
    },

    getAdaptSetting() {
      if (this.config === null) {
        return null;
      }
      const { property } = this.config.config;
      let obj = null;
      if (objHas(property, 'adaptSetting') && property.adaptSetting) {
        obj = JSON.parse(JSON.stringify(property.adaptSetting));
      }
      return obj;
    },

    getTypeSetting() {
      if (this.config === null) {
        return null;
      }
      const { property } = this.config.config;
      let obj = null;
      if (objHas(property, 'typeSetting') && property.typeSetting) {
        obj = JSON.parse(JSON.stringify(property.typeSetting));
      }
      return obj;
    },

    // 根据主题色获取对应
    getValByCompStl(lightVal, darkVal, realVal) {
      const map = {
        light: lightVal,
        dark: darkVal,
        real: realVal,
      };

      const { componentStyle } = this.config.config.property;
      return map[componentStyle];
    },

    // 给body增加visualComponents,visualComponents_body_theme_dark，用于全局样式和洞察云主题区分
    initBodyClass() {
      if (!window.visualComp.bodyClass) {
        const { theme } = window._adoPremise.uc;
        const className = `visualComponents_body_theme_${theme}`;
        window.visualComp.bodyClass = className;
        document.body.classList.add('visualComponents');
        document.body.classList.add(className);
      }
    },

    // 仅在页面在浏览器第一次打开时运行，无刷新跳转不会执行
    bindPageOpen() {
      if (!window.visualComp.isPageOpened) {
        window.visualComp.isPageOpened = true;
        this.setState(getWinRates());
      }
    },

    // 页面分辨率改变时打动作标记
    bindPageResize() {
      if (!window.visualComp.pageResizeInited) {
        window.visualComp.pageResizeInited = true;
        window.addEventListener('resize', this.onComponentResize);
        this.onComponentResize();
      }
    },

    // 页面切换时打动作标记
    bindPageChange() {
      const { pageId } = window.visualComp;
      if (pageId !== this.pageId) {
        if (pageId) {
          visualAction('pageChange', 30000);
          this.setState(getWinRates());
        }
        window.visualComp.pageId = this.pageId;
      }
    },
    onComponentResize(timeout = 0) {
      const fn = () => {
        visualAction('pageResize');
        if (!this.isDesignView) {
          this.setState({
            ...getWinRates(),
            resizeCount: this.resizeCount + 1,
          });
        }
      };
      if (!timeout) {
        fn();
      } else {
        clearTimeout(window.onComponentResizeTimer);
        window.onComponentResizeTimer = setTimeout(fn, timeout);
      }
    },

    // 监听xvue状态，所有组件加载完后执行事件
    onPageWidgetsMounted(callback) {
      EventBus.$once(
        'page-widgets-mounted',
        () => {
          this.hasPageWidgetsMounted = true;
          if (callback) {
            callback();
          }
        },
      );
    },

    // 根据分辨率来更新组件
    _updateComponentByResolution() {
      // 标题模块,由自身接收resize-layout-widget事件

      // 非图表类
      if (this.updateComponentByResolution) {
        this.updateComponentByResolution();
      } else {
        // 图表类
        if (this.$refs.dataChart) {
          this.resizeChart();
        }
      }
    },

    // 所有组件的loadWidget方法后面执行该方法
    afterLoadWidget(callback) {
      this.setExtStyles();
      this.initEchartActions();
      this.initAsTooltip();
      this.initAsTooltipUser();
      this.initLowCodeTips();
      callback ? callback() : null;

      // 复杂编排页面，可能某些逻辑导致没正常改变组件状态，这里做规避处理，防止页面不正常
      setTimeout(() => {
        this.setCompStatus(false);
      }, 1500);
    },

    // 合并config，该方法会在xvue的$_widiget_init执行
    $visualMergeConfig() {
      const dbConfig = this.dbConfig || {};

      /*
       * config.js可能返回一个函数，用函数生成config可以规避历史组件缺少像背景颜色依赖主题这类兼容问题
       * 需要在组件created时生成this.buildConfig变量
       */
      if (this.buildConfig) {
        const _dbConf = this.dbConfig.config || {};
        const componentStyle = (_dbConf.property && _dbConf.property.componentStyle) || window._adoPremise.uc.theme;
        this.orgConfig = this.buildConfig(componentStyle);
      }

      const config = configMerge.mergeConfig(this.orgConfig, dbConfig);
      this.config = config;

      this.loadAdvLinkageFromStorage();
    },

    // 判断是否有新Icon（对象）配置
    haveIcon(icon) {
      if (isObject(icon) && icon.icon_id) {
        return true;
      }
      return false;
    },

    // 组件销毁前去除pageStatus中的条件
    deletePageStatus() {
      const pageId = getPageId(this);
      const { tenantId } = window._adoPremise;

      const sKey = `${pageId}_${tenantId}`;
      const pageStatus = storage.session.get(sKey);
      let evItem = null;
      if (pageStatus) {
        const newPageStatus = {};
        Object.entries(pageStatus).map(([i]) => {
          if (pageStatus[i].widgetInstanceId === this.widgetInstanceId) {
            evItem = pageStatus[i];
          } else {
            newPageStatus[i] = pageStatus[i];
          }
        });
        if (evItem) {
          storage.session.set(sKey, newPageStatus);
        }
      }

      // 同一页面销毁组件时记录需要清理的事件
      if (window.visualComp.pageId === this.pageId) {
        const ev = this.config.events && this.config.events.find(n => n.type === 'out');
        if (ev) {
          const _ev = JSON.parse(JSON.stringify(ev));
          _ev.wid = this.widgetInstanceId;
          this.setState({ needClearOutEvent: _ev });
        }
      } else {
        this.setState({ needClearOutEvent: {} });
      }
    },

    // 把当前组件的tooltip的api数据存入xvue数组
    getTooltipApis() {
      let { tooltip } = this.config.config.property || {};

      // 左表右xx
      if (this.curComp) {
        tooltip = tooltip && tooltip[this.curComp];
      }
      if (!tooltip) {
        return [];
      }
      const { apis: apisTooltip = [] } = tooltip || {};
      const _apisTooltip = []; // 存入
      apisTooltip.forEach(item => {
        const { apiCode } = item || {};
        const _item = { params: (this.params && this.params.params) || [], apiCode };
        if (this.curComp) {
          _item.curComp = this.curComp;
        }
        if (_item.params) {
          _item.params = _item.params.map(ele => {
            const { code, value, wid, isCommon } = ele || {};
            return { code, value, wid, isCommon };
          });
        }
        _apisTooltip.push(_item);
      });
      return _apisTooltip;
    },

    _updateParamsKeyNType(n, type, events) {
      n[type].forEach(n1 => {
        Object.entries(n1).map(([key]) => {
          let evts = events;
          if (n.evtKey) {
            evts = evts.filter(n2 => n2.key === n.evtKey);
          }
          evts.forEach(ev => {
            const dbFields = ev.dbFields || [];
            const item = dbFields.find(n2 => n2.paramKey === key);
            if (item) {
              n1[item.dbField] = n1[key];
              delete n1[key];
            }
          });
        });
      });
    },

    // 接收事件可配置数据库字段转换
    updateParamsKey(obj) {
      if (!this.params) {
        return;
      }
      const { params, conditions } = this.params;
      const confEvents = this.config.events.filter(n1 => n1.key && n1.type === 'in');
      const matchEvents = this.getMatchEvents(obj);

      // 接收参数映射key字典
      const keysMap = {};
      matchEvents.forEach(n => {
        const evt = confEvents.find(n1 => n1.key === n.eventKey);
        if (evt) {
          const dbFields = evt.dbFields || [];
          dbFields.forEach(n2 => {
            keysMap[n2.paramKey] = n2.dbField;
          });
        }
      });

      // 高级事件联动字段映射
      const { sentByWid, linkageDatas = [] } = this.advancedLinkage;
      const _linkageDatas = linkageDatas.filter(n => (n.wid === sentByWid && n.events?.length)) || [];

      // 新的高级事件数据再后面
      const advEvents = _linkageDatas?.[_linkageDatas?.length - 1]?.events || [];
      advEvents.forEach(n => {
        const evt = confEvents.find(n1 => n1.key === n.evtKey);
        if (evt) {
          const dbFields = evt.dbFields || [];
          dbFields.forEach(n2 => {
            keysMap[n2.paramKey] = n2.dbField;
          });
        }
      });

      // xminer
      if (params) {
        params.forEach(n => {
          if (keysMap[n.code]) {
            n.code = keysMap[n.code];
          }
        });
      }

      // xtag
      if (conditions) {
        conditions.forEach(n => {
          ['range', 'terms'].forEach(type => {
            if (n[type]) {
              this._updateParamsKeyNType(n, type, confEvents);
            }
          });
        });
      }
    },

    // 展开收起功能：处理xvue元素属性变化，避免发生xvue重绘把原本收起的组件展开的情况出现
    handleXvueNodeAttributeChange(observeObj) {
      if (!observeObj) {
        return;
      }
      this.xvueMutationObserver = new MutationObserver(() => {
        if (this.isCollapse && this.handleXvueParentHeight) {
          this.handleXvueParentHeight();
        }
      });
      this.xvueMutationObserver.observe(observeObj, {
        attributes: true,
        attributeOldValue: true,
        characterDataOldValue: true,
        attributeFilter: ['style'],
      });
    },

    setCompStatus(hasAsync) {
      this.setComponentStatus({
        pageId: this.pageId,
        widgetInstanceId: this.widgetInstanceId,
        hasAsync: hasAsync,
      });
    },

    getChartData() {
      // 该函数在具体组件代码中覆盖，防止报错
    },

    // 子组件加载
    loadChildWidget(child, config) {
      let childVm = child;
      if (typeof child === 'string') {
        childVm = this.$refs[child];
      }
      childVm.config = config;
      childVm.dbConfig = config; // 防止xvue执行initWdiget方法把config清空
      childVm.loadWidget();
    },

    // 可视化普通联动事件提交
    vsEmit(evtKey, outObj) {
      this.setState({ curEvtEmitObj: outObj });

      // 子组件提交事件有可能找不到xvue的emit方法（子组件可能不挂钩xvue的widget-base mxin文件）
      if (this.emit) {
        if (window.isLocalEnv || !this.isChildComponent) {
          this.emit(evtKey, outObj);
          return;
        }
      }

      // 获取父组件
      let parentVm = this.$parent;
      for (let i = 0; i < 10; i++) {
        if (parentVm.emit) {
          break;
        }
        parentVm = parentVm.$parent;
      }

      this.xVueEmit(evtKey, outObj, parentVm);
    },

    // 子组件提交事件有可能找不到xvue的emit方法（子组件可能不挂钩xvue的widget-base mxin文件）
    xVueEmit(evtKey, outObj, parentVm) {
      // 抄xvue事件发送逻辑
      const orgEvt = this.orgConfig.events.find(n => n.key === evtKey);
      let evt = null;
      if (orgEvt.id) {
        evt = this.config.events.find(n => n.id === orgEvt.id);
      } else {
        evt = this.config.events.find(n => {
          if (n.orgKey) {
            return n.orgKey === evtKey;
          }
          return n.key === evtKey;
        });
      }

      window.xVueWeb.app.config.globalProperties.$xvuePageVappApiAnimationEnabled = true;

      // 改变状态(旧版)
      const pageId = getPageId(parentVm);
      const { tenantId } = window._adoPremise;
      const statusKey = `${pageId}_${tenantId}`;
      const pageStatus = storage.session.get(statusKey) || {};

      let proxyOutObj = {};
      if (evt && evt.parameters && !evt.paramTransfom && evt.parameters.length) {
        for (let i = 0; i < evt.parameters.length; i++) {
          const paramId = evt.parameters[i].id;
          const paramKey = evt.parameters[i].key;
          const orgParamKey = orgEvt.parameters.find(p => p.id === paramId).key;
          pageStatus[paramKey] = outObj[orgParamKey];
          proxyOutObj[paramKey] = outObj[orgParamKey];
        }
      } else if (outObj) {
        proxyOutObj = JSON.parse(JSON.stringify(outObj));
      } else { }
      storage.session.set(statusKey, pageStatus);

      // 发出消息
      const {
        key,
      } = evt;
      if (key) {
        EventBus.$emit(`${key}_xvueInnerEvt_1590721474727`, proxyOutObj);
      }
    },

    initEvtWatch() {
      // 子组件监听
      if (this.isChildComponent) {
        this.$watch('curEvtEmitObj', outObj => {
          this.acceptEvtMsg({}, '', outObj);
        });
      }
    },

    // 组件点击绑定，按需调用
    bindVmClick(fn) {
      this._vmClick = fn;
      this.$el.addEventListener('click', this._vmClick);
    },

    /**
     * 记录接收到的事件
     * @param {object} event   事件对象
     * @param {string} evtFrom 事件来源：  urlEvt(网址事件传参), common(公共参数), normal(普通事件), advancedLinkage(高级事件联动)
     */
    addReceivedEvents(event, evtFrom) {
      event.evtFrom = evtFrom;
      if (evtFrom === 'urlEvt') {
        event.sendTime = 0;
      } else if (evtFrom === 'common') {
        event.sendTime = 1;
      } else { }
      let receivedEvents = this.receivedEvents.filter(n => n.eventKey !== event.eventKey);
      receivedEvents.push(event);
      receivedEvents = _orderBy(receivedEvents, ['sendTime'], ['asc']);
      this.receivedEvents = receivedEvents;
    },

    // 获取跟当前组件有普通事件联动关系的事件数组
    getMatchEvents(obj) {
      const eventArr = [];
      Object.entries(obj).map(([key]) => {
        const o = obj[key];
        if (!key.includes(EMIT_PARAMS_OBJ) || !o.eventKey) {
          return;
        }
        const hasEvent = this.config.events.some(e => e.type === 'in' && e.key === o.eventKey);
        if (hasEvent) {
          eventArr.push(o);
        }
      });
      return eventArr;
    },

    // 记录普通事件
    addNormalEvents(obj) {
      const events = this.getMatchEvents(obj);
      events.forEach(n => {
        this.addReceivedEvents(n, 'normal');
      });
    },

    // 当前编辑中（选中）组件
    initPreviewVm() {
      EventBus.$on(`curPreviewVm_${this.widgetInstanceId}`, () => {
        if (this.isChildComponent) {
          if (this.childIndex === 0) {
            window.visualComp.curPreviewVm = this;
          }
        } else {
          window.visualComp.curPreviewVm = this;
        }
      });
    },

    // 点击空白发送空事件
    clickBlank(e) {
      const excludeSelectors = ['.advanced-linkage-toolbar', '.notBlank']; // 不发送空事件的节点
      let doFlag = true;
      for (let i = 0; i < excludeSelectors.length; i++) {
        const n = excludeSelectors[i];
        if (e.target.closest(n)) {
          doFlag = false;
          break;
        }
      }
      if (!doFlag) {
        return;
      }

      this.sendEmptyMessages();
    },
    bindClickBlank() {
      this.$el.addEventListener('click', this.clickBlank);
    },
    unBindClickBlank() {
      this.$el.removeEventListener('click', this.clickBlank);
    },
    dealPropertyMappingData(result = []) {
      const { data } = this.config.config || {};
      const { dataSourceType, tableData } = data;
      const propertyMapping = convertFormData(tableData);
      if (dataSourceType === 'xVueApi') {
        return result.map(dataItem=>{
          const node = {};
          Object.entries(propertyMapping).forEach(([filed])=>{
            node[filed] = dataItem[filed];
          });
          return node;
        });
      }
      return result;
    },

    // chart 点击发出事件
    chartClickEvent(params, data, isEmitNormalEvent = true) {
      this.chartClick(params, data, isEmitNormalEvent);
      if (params.data) {
        this.sendAdvancedLinkagge(params.data);
      }
    },

    // 接口异常类型修改
    resetAbnormalType() {
      // 当状态异常且存在缓存时，将缓存清空，避免数据正常后不对数据进行处理
      if (this.isDataAbnormal === true && this.apiCacheKey) {
        this.apiCacheKey = '';
      }
      this.isDataAbnormal = false;
      this.abnormalType = ABNORMAL_TYPE.DEFAULT;
    },
    setInterfaceException() {
      this.isDataAbnormal = true;
      this.abnormalType = ABNORMAL_TYPE.INTERFACE;
    },
    setDataException() {
      this.isDataAbnormal = true;
      this.abnormalType = ABNORMAL_TYPE.DATA;
    },
    setFieldException() {
      this.isDataAbnormal = true;
      this.abnormalType = ABNORMAL_TYPE.FIELD;
    },

    handleChartDataError(err) {
      if (err.name === 'AxiosError') {
        this.setInterfaceException();
      }
    },

    /**
     * 判断请求字段与返回字段是否一致
     * @param {Object} propertyMapping 请求字段对象
     * @param {Array} result 响应数据
     * @param {function} callback 非必传，用于替代通用的setFieldException
     * @returns {void}
     */
    reportIfFieldsError(propertyMapping, result, callback) {
      if (typeof propertyMapping !== 'object' || !Array.isArray(result) || result?.length === 0) {
        return;
      }
      const reqFields = Object.keys(propertyMapping).sort();
      const resFields = Object.keys(result[0]).sort();
      if (_difference(reqFields, resFields).length !== 0) {
        callback ? callback() : this.setFieldException();
      }
    },

    // 监听xvue容器展开收起事件重新加载子组件
    watchXvueContainerExpend() {
      let containerVm = this;
      let hasXvueContainer = false;
      for (let i = 0; i < 6; i++) {
        containerVm = containerVm.$parent;
        if (!containerVm) {
          break;
        }
        if (containerVm.isShowExpandPannel === true || containerVm.isShowExpandPannel === false) {
          hasXvueContainer = true;
          break;
        }
      }
      if (!hasXvueContainer) {
        return;
      }
      watch(() => containerVm.isShowExpandPannel, isShow => {
        if (isShow) {
          this.loadWidget();
        }
      });
    },

    // 清除图片blob缓存，防止内存泄漏
    clearImgBlogCache() {
      const imgCaches = window.visualComp.imgCaches || {};
      Object.entries(imgCaches).forEach(([key, val]) => {
        URL.revokeObjectURL(val);
      });
      window.visualComp.imgCaches = {};
    },

  },
};
